﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;

namespace MefBasic.UserControls
{
    public partial class CollapsibleContainer
    {
        private const string LeftIcon = "LeftIcon";
        private const string RightIcon = "RightIcon";
        private const string UpIcon = "UpIcon";
        private const string DownIcon = "DownIcon";
        private const double Delay = 0.25;

        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded", typeof (bool), typeof (CollapsibleContainer),
                                        new UIPropertyMetadata(true, OnPropertyChanged));

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (CollapsibleContainer) d;
            control.DoPropertyChanged(e);
        }

        private void DoPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            switch (e.Property.Name)
            {
                case "IsExpanded":
                    RefreshView();
                    break;
                case "Child":
                    childContentControl.Content = Child;
                    break;
                case "Direction":
                    HideAllBorder();
                    RefreshView();
                    break;
            }
        }

        private void HideAllBorder()
        {
            btnLeft.Visibility = Visibility.Collapsed;
            btnTop.Visibility = Visibility.Collapsed;
            btnRight.Visibility = Visibility.Collapsed;
            btnBottom.Visibility = Visibility.Collapsed;
        }

        public static readonly DependencyProperty ChildProperty =
            DependencyProperty.Register("Child", typeof (object), typeof (CollapsibleContainer),
                                        new UIPropertyMetadata(null,OnPropertyChanged));

        private double _delay;
        private Point _initialMousePosition;
        private bool _isDragging;

        private bool _isLoaded;

        public CollapsibleContainer()
        {
            InitializeComponent();
            InitializeMyComponent();
            imgLeft.Content = FindResource(LeftIcon);
            imgTop.Content = FindResource(UpIcon);
            imgRight.Content = FindResource(RightIcon);
            imgBottom.Content = FindResource(DownIcon);
        }

        public bool IsAnimationOn { set; get; }
        public bool CanCollapse { get; set; }

        public bool IsExpanded
        {
            get { return (bool) GetValue(IsExpandedProperty); }
            set { SetValue(IsExpandedProperty, value); }
        }

        public object Child
        {
            get { return GetValue(ChildProperty); }
            set { SetValue(ChildProperty, value); }
        }

        public event EventHandler AnimationCompleted;


        private void InitializeMyComponent()
        {
            IsAnimationOn = true;
            SubscribeMouseDown();
            contentHolder.Loaded += HandleLoaded;
            contentHolder.SizeChanged += HandleContentHolderSizeChanged;
            _delay = Delay;
        }

        private void HandleContentHolderSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ContentSize==0)
                ContentSize = Math.Max(ContentSize, GetActualSize());
        }

        private void SubscribeMouseDown()
        {
            imgLeft.PreviewMouseDown += BtnShowHideMouseUp;
            imgTop.PreviewMouseDown += BtnShowHideMouseUp;
            imgRight.PreviewMouseDown += BtnShowHideMouseUp;
            imgBottom.PreviewMouseDown += BtnShowHideMouseUp;
        }

        private double GetActualSize()
        {
            switch (Direction)
            {
                case Direction.LeftToRight:
                case Direction.RightToLeft:
                    return contentHolder.ActualWidth;
                case Direction.TopToBottom:
                case Direction.BottomToTop:
                    return contentHolder.ActualHeight;
            }
            return 0;
        }

        private void HandleLoaded(object sender, RoutedEventArgs e)
        {
            if (_isLoaded) return;
            _isLoaded = true;
            ContentSize = GetActualSize();
            UpdateImage();
        }

        private void OnAnimationCompleted()
        {
            if (AnimationCompleted != null)
            {
                AnimationCompleted(this, new EventArgs());
            }
        }


        private void BtnShowHideMouseUp(object sender, MouseButtonEventArgs e)
        {
            IsExpanded = !IsExpanded;
            RefreshView();
            e.Handled = true;
        }


        public ICommand ExpandCollapseChangedCommand
        {
            get { return (ICommand)GetValue(ExpandCollapseChangedCommandProperty); }
            set { SetValue(ExpandCollapseChangedCommandProperty, value); }
        }

        public static readonly DependencyProperty ExpandCollapseChangedCommandProperty =
            DependencyProperty.Register("ExpandCollapseChangedCommand", typeof(ICommand), typeof(CollapsibleContainer), new UIPropertyMetadata(null));



        public object ExpandCollapseChangedCommandParameter
        {
            get { return (object)GetValue(ExpandCollapseChangedCommandParameterProperty); }
            set { SetValue(ExpandCollapseChangedCommandParameterProperty, value); }
        }

        public static readonly DependencyProperty ExpandCollapseChangedCommandParameterProperty =
            DependencyProperty.Register("ExpandCollapseChangedCommandParameter", typeof(object), typeof(CollapsibleContainer), new UIPropertyMetadata(null));


        private void RefreshView()
        {
            UpdateImage();
            if (IsExpanded)
                Expand();
            else
                Collapse();
        }


        public void SetSizable(bool isSizable)
        {
            if (isSizable)
            {
                SubscribeEventsForSizable(btnLeft);
                SubscribeEventsForSizable(btnTop);
                SubscribeEventsForSizable(btnRight);
                SubscribeEventsForSizable(btnBottom);
            }
            else
            {
                UnsubscribeEventsForSizable(btnLeft);
                UnsubscribeEventsForSizable(btnTop);
                UnsubscribeEventsForSizable(btnRight);
                UnsubscribeEventsForSizable(btnBottom);
            }
        }

        private void UnsubscribeEventsForSizable(Border border)
        {
            border.MouseMove -= OnBorderMouseMove;
            border.MouseLeftButtonDown -= OnBorderMouseLeftButtonDown;
            border.MouseLeftButtonUp -= OnBorderMouseLeftButtonUp;
            border.Cursor = Cursors.Arrow;
        }

        private void SubscribeEventsForSizable(Border border)
        {
            border.MouseMove += OnBorderMouseMove;
            border.MouseLeftButtonDown += OnBorderMouseLeftButtonDown;
            border.MouseLeftButtonUp += OnBorderMouseLeftButtonUp;
            border.Cursor = Cursors.SizeWE;
        }


        public void Collapse()
        {
            if (!_isLoaded) return;
            if (IsAnimationOn)
            {
                Animate(ContentSize, 0);
            }
            else
            {
                contentHolder.Visibility = Visibility.Collapsed;
            }
        }

        public void Expand()
        {
            if (!_isLoaded) return;
            if (IsAnimationOn)
            {
                Animate(0, ContentSize);
            }
            else
            {
                contentHolder.Visibility = Visibility.Visible;
            }
        }



        public Direction Direction
        {
            get { return (Direction)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        }

        public double ContentSize { get; set; }

        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(Direction), typeof(CollapsibleContainer), new UIPropertyMetadata(Direction.LeftToRight,OnPropertyChanged));

        public void Animate(double from, double to)
        {
            var dbAnimation = new DoubleAnimation();
            dbAnimation.Completed += DoubleAnimationCompleted;
            dbAnimation.From = from;
            dbAnimation.To = to;
            dbAnimation.Duration = new Duration(TimeSpan.FromSeconds(_delay));
            switch (Direction)
            {
                case Direction.LeftToRight:
                case Direction.RightToLeft:
                    contentContainer.BeginAnimation(WidthProperty, dbAnimation);    
                    break;
                case Direction.TopToBottom:
                case Direction.BottomToTop:
                    contentContainer.BeginAnimation(HeightProperty, dbAnimation);
                    break;
            }
            
        }

        private void DoubleAnimationCompleted(object sender, EventArgs e)
        {
            UpdateImage();
            _delay = Delay;
            if (!_isDragging)
            {
                OnAnimationCompleted();
            }
            if (ExpandCollapseChangedCommand != null)
            {
                ExpandCollapseChangedCommand.Execute(ExpandCollapseChangedCommandParameter);
            }
        }


        private void UpdateImage()
        {
            switch (Direction)
            {
                case Direction.LeftToRight:
                    btnLeft.Visibility = Visibility.Visible;
                    imgLeft.Content = FindResource(IsExpanded ? RightIcon : LeftIcon);
                    break;
                case Direction.RightToLeft:
                    btnRight.Visibility = Visibility.Visible;
                    imgRight.Content = FindResource(IsExpanded ? LeftIcon : RightIcon);
                    break;
                case Direction.TopToBottom:
                    btnTop.Visibility = Visibility.Visible;
                    imgTop.Content = FindResource(IsExpanded ? DownIcon : UpIcon);
                    break;
                case Direction.BottomToTop:
                    btnBottom.Visibility = Visibility.Visible;
                    imgBottom.Content = FindResource(IsExpanded ? UpIcon : DownIcon);
                    break;
            }
        }


        private void OnBorderMouseMove(object sender, MouseEventArgs e)
        {
            if (!IsExpanded) return;
            if (e.LeftButton != MouseButtonState.Pressed) return;

            var currentPosition = e.GetPosition(this);
            var deltaX = currentPosition.X - _initialMousePosition.X;

            var absDelta = Math.Abs(deltaX);
            if (absDelta < SystemParameters.MinimumHorizontalDragDistance) return;
            if (!_isDragging)
            {
                _isDragging = true;
            }
            _initialMousePosition = currentPosition;
            var currentWidth = contentHolder.ActualWidth;
            var newWidth = contentHolder.ActualWidth;
            if (IsWidthIncreasing(deltaX))
            {
                if ((newWidth + absDelta) <= (Application.Current.MainWindow.ActualWidth/2))
                    newWidth += absDelta;
            }
            else
            {
                if (newWidth - absDelta >= contentHolder.MinWidth)
                    newWidth -= absDelta;
            }
            _delay = 0;
            Animate(currentWidth, newWidth);
        }

        private bool IsWidthIncreasing(double deltaX)
        {
            if (Direction == Direction.RightToLeft)
            {
                return deltaX > 0;
            }
            return deltaX < 0;
        }

        private void OnBorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var border = (Border) sender;
            Mouse.Capture(border);
            _initialMousePosition = e.GetPosition(this);
        }

        private void OnBorderMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _isDragging = false;
            Mouse.Capture(null);
        }
    }
}